package sk.upjs.ics.fca.concepts;

import java.util.LinkedList;
import java.util.ListIterator;
import java.util.NoSuchElementException;

/**
 * Trieda, ktora riesi vsetky logicke operacie
 * 
 * @author Patrik Mihalcin
 */
public class Logic {

	private LinkedList<Double> truthDegrees = new LinkedList<Double>();
	private Integer logicDegree = 2;

	/**
	 * Konstruktor podla zadaneho stupna logiky vygeneruje mnozinu
	 * pravdivostnych hodnot
	 * 
	 * @param logicDegree
	 */
	public Logic(Integer logicDegree) {
		this.logicDegree = logicDegree;

		Double diff = 1.0 / (logicDegree - 1);
		diff = round(diff, 2);

		Double value = round(1.0, 2);

		for (int i = 0; i < logicDegree - 1; i++) {
			this.truthDegrees.add(value);

			value = value - diff;
			value = round(value, 2);
		}

		this.truthDegrees.add(round(0.0, 2));
	}

	private Double round(Double number, Integer precision) {
		if (precision <= 0) {
			return null;
		}
		StringBuilder s = new StringBuilder("1");
		for (int i = 0; i < precision; i++) {
			s.append("0");
		}
		precision = Integer.parseInt(s.toString());
		return (double) Math.round(number * precision) / precision;
	}

	/**
	 * Fuzzy implikacia medzi vstupnymi pravdivostnymi stupnami
	 * 
	 * @param a
	 *            1. hodnota
	 * @param b
	 *            2. hodnota
	 * @return implikacia medzi vstupnymi pravdivostnymi stupnami
	 */
	public Double implication(Double a, Double b) {
		// fuzzy implikacia - reziduum
		return Math.min(1.0 - a + b, 1.0);
	}

	/**
	 * Infimum (minimum) medzi vstupnymi pravdivostnymi stupnami
	 * 
	 * @param a
	 *            1. hodnota
	 * @param b
	 *            2. hodnota
	 * @return infimum (minimum) medzi vstupnymi pravdivostnymi stupnami
	 */
	public Double infimum(Double a, Double b) {
		return Math.min(a, b);
	}

	/**
	 * Disjunkcia medzi vstupnymi pravdivostnymi stupnami
	 * 
	 * @param a
	 *            1. hodnota
	 * @param b
	 *            2. hodnota
	 * @return disjunkcia medzi vstupnymi pravdivostnymi stupnami
	 */
	public Double disjunction(Double a, Double b) {
		return implication(implication(a, 0.0), b);
	}

	/**
	 * Silna konjunkcia (maximum) medzi vstupnymi pravdivostnymi stupnami
	 * 
	 * @param a
	 *            1. hodnota
	 * @param b
	 *            2. hodnota
	 * @return silna konjunkcia (maximum) medzi vstupnymi pravdivostnymi
	 *         stupnami
	 */
	public Double strongConjunction(Double a, Double b) {
		// fuzzy konjunkcia
		return Math.max(0.0, a + b - 1.0);
	}

	// private Double supremum(Double a, Double b) {
	// return Math.max(a, b);
	// }
	//
	// private Double ekvivalencia(Double a, Double b) {
	// // bireziduum
	// // return 1.0 - Math.abs(a - b);
	// return Math.min(implikacia(a, b), implikacia(b, a));
	// }

	/**
	 * Vrati dalsiu pravdivostnu hodnotu vstupnej hodnoty v ramci mnoziny
	 * pravdivostnych hodnot
	 * 
	 * @param truthValue
	 *            vstupna pravdivostna hodnota
	 * @return dalsia pravdivostna hodnota
	 */
	public Double getNextTruthValue(Double truthValue) {
		if (truthValue >= this.truthDegrees.getFirst()) {
			throw new NoSuchElementException("Truth value " + truthValue
					+ " does not have successor.");
		}

		Double vysledok = null;
		for (ListIterator<Double> it = truthDegrees.listIterator(truthDegrees
				.size()); it.hasPrevious();) {
			// prechadzame truthDegrees zozadu
			if (it.previous().equals(round(truthValue, 2))) {
				vysledok = it.previous();
			}
		}
		return vysledok;
	}

	/**
	 * Vrati mnozinu pravdivostnych hodnot
	 * 
	 * @return mnozina pravdivostnych hodnot
	 */
	public LinkedList<Double> getTruthDegrees() {
		return truthDegrees;
	}

	/**
	 * Vrati stupen fuzzy logiky
	 * 
	 * @return stupen fuzzy logiky
	 */
	public Integer getLogicDegree() {
		return logicDegree;
	}
}